package org.moonlight.jvm;

import org.moonlight.jvm.classpath.Classpath;
import org.moonlight.jvm.cmd.Cmd;
import org.moonlight.jvm.interpret.Interpret;
import org.moonlight.jvm.rtda.exclusive.Frame;
import org.moonlight.jvm.rtda.exclusive.Thread;
import org.moonlight.jvm.rtda.share.Object;
import org.moonlight.jvm.rtda.share.heap.Class;
import org.moonlight.jvm.rtda.share.heap.ClassLoader;
import org.moonlight.jvm.rtda.share.heap.classmember.Method;
import org.moonlight.jvm.rtda.share.methodarea.StringPool;

/**
 * 逻辑描述:
 * 注意事项:
 * 需求来源:
 * 修改记录: 修改者 - 修改时间 - 修改内容
 *
 * @author moonlight
 * @since 2023/11/10 14:45 星期五
 **/
public class Jvm {

    private final Cmd cmd;
    private final ClassLoader classLoader;
    private final Thread mainThread;

    public Jvm(Cmd cmd) {
        this.cmd = cmd;

        Classpath cp = new Classpath(cmd.getJre(), cmd.getClasspath());
        this.classLoader = new ClassLoader(cp, cmd.isVerboseClass());

        this.mainThread = new Thread();
    }

    public void start() {
        // 先加载 sun.misc.VM类, 然后执行它的类初始化方法
        this.initVm();
        // 再加载主类并执行 main 方法
        this.execMain();
    }

    private void initVm() {
        Class vmClass = this.classLoader.loadClass("sun/misc/VM");
        vmClass.classInit(this.mainThread);
        new Interpret().interpret(this.mainThread, this.cmd.isVerboseInst());
    }

    private void execMain() {
        String mainClassName = this.cmd.getMainClass().replace(".", "/");
        Class mainClass = this.classLoader.loadClass(mainClassName);
        Method mainMethod = mainClass.getMainMethod();
        if (mainMethod == null) {
            throw new RuntimeException("Main method not found in class " + this.cmd.getMainClass());
        }
        Object args = this.createArgsArray();
        Frame frame = this.mainThread.newFrame(mainMethod);
        frame.getLocalVars().setRef(0, args);
        this.mainThread.pushFrame(frame);
        new Interpret().interpret(this.mainThread, this.cmd.isVerboseInst());
    }

    private Object createArgsArray() {
        Class stringClass = this.classLoader.loadClass("java/lang/String");

        String[] args = this.cmd.getArgs();
        int argsLength = args.length;
        Object array = stringClass.arrayClass().newArray(argsLength);

        Object[] refs = array.refs();

        for (int i = 0; i < argsLength; i++) {
            refs[i] = StringPool.myString(this.classLoader, args[i]);
        }
        return array;
    }

}
